package cn.lg.soar.redis.manager;

import cn.lg.soar.common.util.current.CurrentTokenInfo;
import cn.lg.soar.common.util.data.DataUtil;
import cn.lg.soar.common.util.hardware.SystemUtils;
import cn.lg.soar.common.util.token.ISoarJWT;
import cn.lg.soar.common.util.token.SoarJWTFast;
import cn.lg.soar.core.manager.IStateAccessTokenManager;
import cn.lg.soar.core.model.AccessTokenProps;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import java.time.Duration;
import java.util.concurrent.TimeUnit;

/**
 * @author luguoxiang 469599794@qq.com
 * @Date: 2025/5/17 14:48
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class RedisStateAccessTokenManager implements IStateAccessTokenManager {

    private final static String PREFIX = "TK:";
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisStateAccessTokenManager.class);

    private final long expire;
    private final ISoarJWT soarJWT;
    // 缓存，白名单是 true，黑名单是 false
    private final Cache<Long, Boolean> cache;
    // 黑名单模式
    private final RedisTemplate<String, Object> redisTemplate;
    private final ValueOperations<String, Object> cacheTemplate;

    public RedisStateAccessTokenManager(AccessTokenProps config, RedisTemplate<String, Object> redisTemplate) {
        long cacheSize = SystemUtils.getTotalMemory() >>> 17;
        this.cache = Caffeine.newBuilder()
                .maximumSize(cacheSize)
                .initialCapacity(128)
                .expireAfterWrite(Duration.ofSeconds(30))
                .build();
        LOGGER.info("token cache max size {}", cacheSize);
        this.expire = config.getAccessTokenExpire().toMillis();
        this.soarJWT = new SoarJWTFast(config.getAccessTokenSecret(), this.expire);
        this.cacheTemplate = redisTemplate.opsForValue();
        this.redisTemplate = redisTemplate;
    }

    /**
     * 创建token
     * @param payload 刷新token
     * @return 返回 accessToken
     */
    @Override
    public String create(String payload) {
        String accessToken = soarJWT.create(payload);
        CurrentTokenInfo currentTokenInfo = CurrentTokenInfo.valueOf(payload);
        long tokenId = currentTokenInfo.getTokenId();
        // 加入白名单
        redisTemplate.delete(PREFIX + DataUtil.longToBase64(tokenId));
        cache.put(tokenId, true);
        return accessToken;
    }

    /**
     * 验证token
     * @param accessToken
     * @return
     */
    @Override
    public CurrentTokenInfo verify(String accessToken) {
        String payload = soarJWT.verify(accessToken);
        if (payload == null) {
            return null;
        }
        CurrentTokenInfo currentTokenInfo = CurrentTokenInfo.valueOf(payload);
        Boolean ok = cache.get(currentTokenInfo.getTokenId(), k -> {
            Boolean b = redisTemplate.hasKey(PREFIX + DataUtil.longToBase64(k));
            return !b;
        });
        if (ok) {
            return currentTokenInfo;
        };
        return null;
    }

    /**
     * 删除token
     * @param tokenId
     */
    @Override
    public void removeToken(long tokenId) {
        cacheTemplate.set(PREFIX + DataUtil.longToBase64(tokenId), 1, expire, TimeUnit.MILLISECONDS);
        cache.put(tokenId, false);
    }

}
